package cn.webestar.scms.security.controller;

import cn.hutool.core.util.RandomUtil;
import cn.webestar.scms.commons.Assert;
import cn.webestar.scms.commons.R;
import cn.webestar.scms.security.*;
import cn.webestar.scms.security.api.AccountLoginDto;
import cn.webestar.scms.security.api.LoginVo;
import cn.webestar.scms.security.api.PhoneNumLoginDto;
import cn.webestar.scms.security.api.SendSmsCodeDto;
import cn.webestar.scms.security.captcha.ImageCaptcha;
import cn.webestar.scms.security.captcha.ImageCaptchaGenerator;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author zgs
 */
@Slf4j
@Validated
@RestController
@RequestMapping("/")
public class SecurityController {

    /**
     * 系统环境
     */
    @Value("${spring.profiles.active:}")
    private String env;

    /**
     * 短信超级验证码
     */
    @Value("${app.sms.superCode:}")
    private String smsSuperCode;

    @Autowired
    private SecurityProperties properties;

    @Autowired
    private ImageCaptchaGenerator imageCaptchaGenerator;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired(required = false)
    private SecurityService securityService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @PostMapping("/unpapi/login/account")
    public R<LoginVo> accountLogin(@RequestBody @Validated AccountLoginDto dto) {

        Assert.notEmpty(dto.getUsername(), 30002, "username不能为空");
        Assert.notEmpty(dto.getPassword(), 30003, "pwd不能为空");

        if (properties.getEnableImageCaptcha()) {
            Assert.notNull(dto.getRequestId(), 30004, "请求参数requestId不能为空");

            String imgCaptchaKey = String.format(Constant.CACHE_KEY_IMAGE_CAPTCHA, properties.getCachePrefix(), dto.getRequestId());
            String imgCaptcha = redisTemplate.opsForValue().get(imgCaptchaKey);
            Assert.isTrue(imgCaptcha != null && imgCaptcha.equals(dto.getCaptcha()), 30004, "图形验证码错误");
            //销毁图形验证码，以免别人使用次图像验证码刷接口
            redisTemplate.expire(imgCaptchaKey, 0, TimeUnit.SECONDS);
        }

        if (RsaUtils.ENCRYPT_TYPE.equals(properties.getEncryptType())) {
            String password = RsaUtils.decrypt(dto.getPassword(), properties.getPrivateKey());
            Assert.notNull(password, 30010, "用户名或者密码错误");
            dto.setPassword(password);
        }

        SecurityUser user = securityService.loadSecurityUser(dto);
        Assert.notNull(user, 30010, "用户名或者密码错误");

        //校验密码
        boolean flag = passwordEncoder.matches(dto.getPassword(), user.getPassword());
        Assert.isTrue(flag, 30010, "用户名或者密码错误");

        LoginVo vo = convert(user, user.getPermissions());

        return R.success(vo);
    }

    @PostMapping("/unpapi/login/phoneNum")
    public R<LoginVo> phoneNumLogin(@Validated @RequestBody PhoneNumLoginDto dto) {
        Assert.notEmpty(dto.getPhoneNum(), 30002, "手机号不能为空");
        Assert.notEmpty(dto.getSmsCode(), 30002, "短信验证码不能为空");

        String smsCodeCacheKey = String.format(Constant.CACHE_KEY_SMS_CODE, properties.getCachePrefix(), dto.getPhoneNum());
        String smsCode = redisTemplate.opsForValue().get(smsCodeCacheKey);
        boolean skip = ("dev".equals(env) || "test".equals(env)) && dto.getSmsCode().equals(smsSuperCode);
        if (!skip) {
            Assert.isTrue(dto.getSmsCode().equals(smsCode), 30002, "短信验证码错误");
        }

        SecurityUser user = securityService.loadSecurityUser(dto);
        Assert.notNull(user, 30010, "手机号错误");

        LoginVo vo = convert(user, user.getPermissions());
        redisTemplate.expire(smsCodeCacheKey, 0, TimeUnit.SECONDS);
        return R.success(vo);
    }

    @GetMapping("/unpapi/captcha/image")
    public String image(@RequestParam("requestId") String requestId, HttpServletResponse response) throws IOException {
        Assert.notNull(requestId, 30001, "缺少参数requestId");
        ImageCaptcha imageCaptcha = imageCaptchaGenerator.generate();
        redisTemplate.opsForValue().set(String.format(Constant.CACHE_KEY_IMAGE_CAPTCHA, properties.getCachePrefix(), requestId), imageCaptcha.getCode(), 180, TimeUnit.SECONDS);
        log.debug("生成图形验证码：{}, 有效期:{}妙", imageCaptcha.getCode(), 180);
        ImageIO.write(imageCaptcha.getImage(), "JPEG", response.getOutputStream());
        return requestId;
    }

    @PostMapping("/unpapi/captcha/smsCode")
    public R<Object> smsCode(@RequestBody @Validated SendSmsCodeDto dto) {
        Assert.notNull(dto.getPhoneNum(), 30001, "缺少参数phoneNum");
        String smsCode = RandomUtil.randomNumbers(6);
        Object o = securityService.sendSmsCode(dto.getType(), dto.getPhoneNum(), smsCode);
        redisTemplate.opsForValue().set(String.format(Constant.CACHE_KEY_SMS_CODE, properties.getCachePrefix(), dto.getPhoneNum()), smsCode, 300, TimeUnit.SECONDS);
        log.debug("生成短信验证码:{}, 手机号:{}, 有效期:{}秒", smsCode, dto.getPhoneNum(), 300);
        // 有效期5分钟
        return R.success(o);
    }

    @ResponseBody
    @PostMapping(value = "/api/common/myCorps")
    public R<List<SecurityCorp>> myCorps() {
        Authentication auth = SessionContextHolder.getContext().getAuth();
        SecurityUser principal = auth.getPrincipal();
        List<SecurityCorp> corps = securityService.loadUserCorps(principal.getUserId());
        return R.success(corps);
    }

    @ResponseBody
    @PostMapping(value = "/api/common/switchCorp")
    public R<Boolean> switchCorp(@RequestParam("id") Long id) {
        return R.success(securityService.switchCorp(id));
    }

    @GetMapping("/api/logout")
    public R<Boolean> logout() {
        SessionContextHolder.logout();
        return R.success(true);
    }

    private LoginVo convert(SecurityUser user, Set<String> permissions) {
        //设置认证信息
        Authentication auth = new Authentication();
        auth.setId(user.getId());
        auth.setPermissions(permissions);
        auth.setPrincipal(user);
        SessionContext context = SessionContextHolder.auth(auth);

        //创建返回数据
        LoginVo vo = new LoginVo();
        vo.setAccessToken(context.getAccessToken());
        vo.setRefreshToken(context.getRefreshToken());
        vo.setExpireIn(properties.getAccessTokenExpireIn());
        return vo;
    }

}
